package chap_01_CompletableFuture.step_01_FutureTask;

import java.util.concurrent.*;

/**
 * @Description 线程池方式使用 FutureTask
 * @Author shihan
 * @Date 2022/9/3 16:24
 * @Version 1.0
 */
public class FutureTaskStudy_02_pool {

    /**
     * 两个问题：
     * 1、线程池+异步线程并不一定能缩短主线程的执行耗时，因为线程池中线程不可用时，需要排队等候，有时候反而会更慢；
     * 2、线程池的 submit 提交带返回值的线程时（下面的解释不一定对，只是说需要后续使用的时候注意一下 TODO 探究下面现象的根本原因--源码分析）：
     * 2.1、丢进去的是 FutureTask 对象，线程执行的返回值只在传参的 FutureTask 对象中，线程池返参的 Future 对象中的 get 为 null；
     * 2.2、丢进去的是 Callable 对象，线程执行的返回值，在线程池返参的 Future 对象中；
     * 主要的问题：submit 有重载的方法，一个是 submit(Runnable) 另一个是 submit(Callable)，然而 FutureTask 属于 Runnable；
     * PS：
     * 1、Future 的 get 会阻塞直到返回值方法执行结束，获取到返回值为止；
     * 2、如果不执行 get 就不会阻塞等待返回值，等同于丢了一个普通的 Runnable 线程进去，至于他啥时候执行结束不care也不影响主线程，这个需要注意一下；
     * 3、异常处理，只在 get 时需要捕获，如果不进行 get 获取其实是无法获取到异常的；
     * 【FutureTask的缺点】：说白了就是在获取返回值的时候，不够优雅
     * 1、get 获取返回值时，会一直阻塞；
     * 2、为了避免 get 一直阻塞，可以使用 get(long,TimeUnit) 的给定阻塞时间的方法，超过指定时间不返回抛异常，但是还是不友好；
     * 3、isDone 是否执行结束可以获取返回值的判断，会比给定阻塞时间要优雅一点，但是需要不停的轮训，并且间隔一段时间去 isDone 判断，还是不优雅；
     * 思考：SpringEvent 好像也可以异步执行（默认是同步，主要是解耦），并且指定线程池，他和 FutureTask 有啥关联吗，能够获取返回值并捕获异常吗？
     *
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //Executors.newFixedThreadPool(3);
        /*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1,
                1,
                100,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(3)
        );*/
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3,
                3,
                100,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(3)
        );

        //testOne(threadPoolExecutor);

        //testTwo(threadPoolExecutor);

        testThree(threadPoolExecutor);

        // 生产不会去 shutdown 线程池的
        threadPoolExecutor.shutdown();

    }

    /**
     * 异常处理，只在 get 时需要捕获，如果不进行 get 获取其实是无法获取到异常的
     *
     * @param threadPoolExecutor
     */
    private static void testThree(ThreadPoolExecutor threadPoolExecutor) {
        Future<String> submit = threadPoolExecutor.submit(() -> {
            int i = 10 / 0;
            return "让你出异常！";
        });

        String s = null;
        try {
            // get 将一直阻塞，直到获取返回值成功
            s = submit.get();
            // 在给定的时间内一直阻塞等待，超过给定时间后，抛异常（简单了解即可，这里是为了避免一直阻塞的问题）
            submit.get(3, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (ArithmeticException e) {
            e.printStackTrace();
            System.out.println("数学运算异常！");
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        System.out.println(s);
    }

    /**
     * 个人更推荐直接丢 Callable 对象的方式，减少 FutureTask 对象的创建，反正线程池最终会返回一个 Future 指向的一个对象
     *
     * @param threadPoolExecutor
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void testTwo(ThreadPoolExecutor threadPoolExecutor) throws InterruptedException, ExecutionException {
        // 丢进去的是一个 Callable 对象
        Future<String> ddd = threadPoolExecutor.submit(() -> {
            System.out.println("lambda表达式方式...");
            return "ddd";
        });
        System.out.println(ddd.get());
    }

    private static void testOne(ThreadPoolExecutor threadPoolExecutor) throws InterruptedException, ExecutionException {
        long t1 = System.currentTimeMillis();
        FutureTask<String> futureTask = new FutureTask<>(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("abc");
            return "abc";
        });
        FutureTask<String> futureTask1 = new FutureTask<>(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("def");
            return "def";
        });
        FutureTask<String> futureTask2 = new FutureTask<>(() -> {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ghi");
            return "ghi";
        });
        // 丢进去的是 FutureTask 对象
        Future<?> submit = threadPoolExecutor.submit(futureTask);
        Future<?> submit1 = threadPoolExecutor.submit(futureTask1);
        Future<?> submit2 = threadPoolExecutor.submit(futureTask2);

        // 能够获取到返回值
        System.out.println(futureTask2.get());
        // null 获取不到返回值
        System.out.println(submit2.get());

        long t2 = System.currentTimeMillis();
        System.out.println("总耗时：" + (t2 - t1));
    }

}
